local redis = require "resty.redis"

local next  = next

local _M = {}

do
  local _M_mt = { __index = _M }


  --[[
    Trigger the error.
      params:
       - self (_M:object)
         The connector.
       - error (any)
         The error.
  ]]
  local function _trigger_error(self, err)
    assert(self)
    if next(self.error_handler) then
      for _, handler in ipairs(self.error_handler) do
        handler(err)
      end
    end
  end


  --[[
    Preform connecting to Mysql server.
      params:
       - self (_M:object)
         The connector.
      returns:
        The Connector object.
  ]]
  local function _connect_safe(self, connection)
    assert(self)
    assert(connection, "MISSING_CONNECTION")

    local server, err = redis:new()
    if err then
      _trigger_error(self, err)
      return
    end

    if connection.timeout then
      server:set_timeout(connection.timeout)
    end

    local host   = connection.host or "127.0.0.1"
    local port   = connection.port or 6379
    local secret = connection.secret
    local opt    = connection.opt

    local ok, err = server:connect(host, port, opt)
    if not ok then
      _trigger_error(self, err)
      return
    end

    if secret and #secret > 0 then
      local ok, err = server:AUTH(secret)
      if not ok then
        _trigger_error(self, err)
      end
    end

    local _, err = server:PING()
    if err then
      _trigger_error(self, err)
    end
    return server
  end


  --[[
    Connect to the databases.
      params:
        - host (string)
          the host name for the Redis server.
        - port (int)
          the port that the Redis server is listening on. [Default to 6379]
        - secret (string|nil)
          Redis password for login (in clear text).
        - timeout
          sets the timeout (in ms) protection for subsequent operations
        - opt (table|nil)
          The options argument is a Lua table holding the following keys:
          [ https://github.com/openresty/lua-resty-redis#connect ]
      return:
        redis client
  ]]
  function _M.new(connection, error_handler)
    local self = setmetatable({
      error_handler = {error_handler}
    }, _M_mt)

    local client = _connect_safe(self, connection)
    return setmetatable(self, {
      __index = function(_, cmd)
        local f = function(self, ...)
          local handler = client[cmd]
          local reply, err = handler(client, ...)
          if err then
            _trigger_error(self, err)
          end
          return reply, err
        end
        self[cmd] = f
        return f
      end
    })
  end


  --[[
    Puts the current Redis connection immediately into the ngx_lua cosocket
    connection pool.
      params:
       - timeout (number)
         the max idle timeout (in ms) when the connection is in the pool.
       - pool_size (number)
         the maximal size of the pool every nginx worker process.
      returns:
        In case of success, returns 1. In case of errors, returns nil with a
        string describing the error.
  ]]
  function _M.set_keepalive(self, timeout, poll_size)
    if self.server then
      local ok, err = self.server:set_keepalive(timeout, poll_size)
      if err then
        _trigger_error(self, err)
        return
      end
      return ok
    end
  end


  --[[
    Add the Error listeners.
      params:
       - handler (function)
         The error listeners.
      returns:
        The self.
  ]]
  function _M.add_error_handler(self, handler)
    if  type(handler) == 'function'  then
      table.insert(self.error_handler, handler)
    end
    return self
  end


  --[[
    Closes the current redis connection and returns the status.
      returns:
        In case of success, returns 1. In case of errors, returns nil with a
        string describing the error.
  ]]
  function _M.close(self)
    if self.server then
      local ok, err = self.server:close()
      if err then
        _trigger_error(self, err)
        return
      end
      return ok
    end
  end

  _M.trigger_error = _trigger_error
end
return _M
